from django import template
from django.db import models
from django.utils.encoding import smart_str
import re

register = template.Library()
Entry = models.get_model('friendly', 'entry')
Comment = models.get_model('friendly', 'comment')
Media = models.get_model('friendly', 'media')


class DoGetFirstMediaThumbnail:
    """
    Gets the first thumbnail image for the first media object

    Syntax::

        {% get_friendfeed_first_media_thumbnail for [friendfeed_id] as [varname] %}
   
    Example usage::

        {% get_friendfeed_first_media_thumbnail for photo.ff_id as thumbnail %} # 5
        
    """
    def __call__(self, parser, token):
        tokens = token.contents.split()
        # ['get_friendfeed_first_media_thumbnail', 'for', 'youtube.ff_id',     'as'    'thumbnail']
        if not len(tokens) in (5,):
            raise template.TemplateSyntaxError, "%r tag requires 5arguments" % tokens[0]
        if tokens[1] != 'for':
            raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0]
        var_name, obj_id, by = None, None, None
        if '.' not in tokens[2]:
            obj_id = tokens[3]
            try: # ensure the object ID is valid
                Entry.objects.get(pk=obj_id)
            except ObjectDoesNotExist:
                raise template.TemplateSyntaxError, "%r tag refers to Entry object with ID %s, which doesn't exist" % (tokens[0], obj_ff_id)
        else:
            var_name = tokens[2]
        if tokens[3] != 'as':
            raise template.TemplateSyntaxError, "Third argument in %r must be 'as'" % tokens[0]
            
        print "Varname: %s" % var_name
        print "Obj ID: %s" % obj_id
        print "Context var: %s" % tokens[4]
            
        return ThumbnailNode(var_name, obj_id, tokens[4])

class DoGetMediaList:
    """
    Gets media for the given params and populates the template context with a
    special variable, whose name is defined by the ``as`` clause.

    Syntax::

        {% get_friendfeed_media_list for [friendfeed_id] as [varname] (limit) %}
   
    Example usage::

        {% get_friendfeed_media_list for photo.ff_id as flickr_media 5 %} # 6
        {% get_friendfeed_media_list for youtube.ff_id as youtube_media %} # 5
        
    """
    def __call__(self, parser, token):
        tokens = token.contents.split()
        # ['get_friendfeed_media_list', 'for', 'youtube.ff_id',     'as'    'flickr_media',  '5']
        # ['get_friendfeed_media_list', 'for', 'photo.ff_id',       'as',   'youtube_media']
        if not len(tokens) in (5, 6):
            raise template.TemplateSyntaxError, "%r tag requires 5 or 6 arguments" % tokens[0]
        if tokens[1] != 'for':
            raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0]
        var_name, obj_id, by = None, None, None
        if '.' not in tokens[2]:
            obj_id = tokens[3]
            try: # ensure the object ID is valid
                Entry.objects.get(pk=obj_id)
            except ObjectDoesNotExist:
                raise template.TemplateSyntaxError, "%r tag refers to Entry object with ID %s, which doesn't exist" % (tokens[0], obj_ff_id)
        else:
            var_name = tokens[2]
        if tokens[3] != 'as':
            raise template.TemplateSyntaxError, "Third argument in %r must be 'as'" % tokens[0]
        if len(tokens) == 6:
            if tokens[5].isdigit():
                limit = int(tokens[5])
            else:
                raise template.TemplateSyntaxError, "Limit argument in %r must be a digit" % tokens[0]
        else:
            limit = None
            
        return MediaListNode(var_name, obj_id, tokens[4], limit)

class DoGetCommentList:
    """
    Gets comments for the given params and populates the template context with a
    special variable, whose name is defined by the ``as`` clause.

    Syntax::

        {% get_friendfeed_comment_list for [friendfeed_id] as [varname] (by "[user]") %}
   
    Example usage::

        {% get_friendfeed_comment_list for link.ff_id as my_comments by 'clint' %} # len = 7
        {% get_friendfeed_comment_list for link.ff_id as my_comments %} # len = 5
        
    """
    def __call__(self, parser, token):
        tokens = token.contents.split()
        # ['get_friendfeed_comment_list', 'for', 'link.ff_id',  'as'    'link_comments',    'by',   'clint']
        # ['get_friendfeed_comment_list', 'for', 'photo.ff_id', 'as',   'photo_comments']
        if not len(tokens) in (5, 7):
            raise template.TemplateSyntaxError, "%r tag requires 5 or 7 arguments" % tokens[0]
        if tokens[1] != 'for':
            raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0]
        var_name, obj_id, by = None, None, None
        if '.' not in tokens[2]:
            obj_id = tokens[3]
            try: # ensure the object ID is valid
                Entry.objects.get(pk=obj_id)
            except ObjectDoesNotExist:
                raise template.TemplateSyntaxError, "%r tag refers to Entry object with ID %s, which doesn't exist" % (tokens[0], obj_ff_id)
        else:
            var_name = tokens[2]
        if tokens[3] != 'as':
            raise template.TemplateSyntaxError, "Third argument in %r must be 'as'" % tokens[0]
        if len(tokens) == 7:
            by = tokens[6]
            if not (by[0] == by[-1] and by[0] in ('"', "'")):
                raise template.TemplateSyntaxError, "%r tag's by argument should be in quotes" % tag_name
            by = by[1:-1]
            
        return CommentListNode(var_name, obj_id, tokens[4], by)

class DoGetEntryList:
    """
    Gets entries for the given params and populates the template context with a
    special variable, whose name is defined by the ``as`` clause.

    Syntax::

        {% get_friendfeed_entry_list for "[service1|service2|...|'all']" (except "service1|service2|...") (number) as [varname] %}
   
    Example usage::

        {% get_friendfeed_entry_list for "flickr" 10 as flickr_photos %}  # len = 6
        {% get_friendfeed_entry_list for "all" as friendfeed_entries %}   # len = 5
        {% get_friendfeed_entry_list for "all" except "flickr" 20 as friendfeed_entries } # len = 8
        {% get_friendfeed_entry_list for "all" except "flickr" as friendfeed_entries } # len = 7
        {% get_friendfeed_entry_list for "digg" as diggs %} # len = 5
        {% get_friendfeed_entry_list for "flickr|picasa" 20 as photos %} # len = 6
    """
    def __call__(self, parser, token):
        tokens = token.contents.split()
        # Now tokens is a list like this:
        #  0                             1      2                    3           4                  5       6   7
        # ['get_friendfeed_entry_list', 'for', '"all"',             'as',       'flickr_photos']
        # ['get_friendfeed_entry_list', 'for', '"flickr"',          '10',       'as',               'flickr_photos']
        # ['get_friendfeed_entry_list', 'for', '"flickr|picasa"',   '10',       'as',               'photos']
        # ['get_friendfeed_entry_list', 'for', '"all"',             'except',   '"flickr|picasa"',  'as', 'friendfeed_entries']
        # ['get_friendfeed_entry_list', 'for', '"all"',             'except',   '"flickr|picasa"',  '20', 'as', 'friendfeed_entries']
        if not len(tokens) in (5, 6, 7, 8):
            raise template.TemplateSyntaxError, "%r tag requires 5, 6, 7, or 8 arguments" % tokens[0]
        if tokens[1] != 'for':
            raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0]

        var_name = None
        tag_name = tokens[0]
        filters = tokens[2]
        if not (filters[0] == filters[-1] and filters[0] in ('"', "'")):
            raise template.TemplateSyntaxError, "%r tag's service filter argument should be in quotes" % tag_name
        filters = filters[1:-1].split('|')
        
        if tokens[3] == 'except':
            if tokens[5].isdigit():
                limit = tokens[5]
            else:
                pass
                #raise template.TemplateSyntaxError, "%r tag's limit argument must be an integer" % tag_name
            
            excludes = tokens[4]
            if not (excludes[0] == excludes[-1] and excludes[0] in ('"', "'")):
                raise template.TemplateSyntaxError, "%r tag's service exclude argument should be in quotes" % tag_name
            excludes = excludes[1:-1].split('|')
            
            if tokens[5] == 'as':
                try:
                    limit = None
                    var_name = tokens[6]
                except:
                    raise template.TemplateSyntaxError, "%r tag's final argument should be a variable name" % tag_name
            elif tokens[5].isdigit():
                limit = tokens[5]
                var_name = tokens[7]
                
        if tokens[3] == 'as':
            try:
                var_name = tokens[4]
            except:
                raise template.TemplateSyntaxError, "%r tag's final argument should be a variable name" % tag_name
            limit = None
            excludes = []

        if tokens[3].isdigit():
            limit = tokens[3]
            if tokens[4] == 'as':
                try:
                    var_name = tokens[5]
                except:
                    raise template.TemplateSyntaxError, "%r tag's final argument should be a variable name" % tag_name
            excludes = []    
        return EntryListNode(filters, excludes, limit, var_name)

class ThumbnailNode(template.Node):
    def __init__(self, context_var_name, obj_id, var_name):
        if context_var_name is not None:
            context_var_name = template.Variable(context_var_name)
        self.context_var_name, self.obj_id = context_var_name, obj_id
        self.var_name = var_name

    def render(self, context):
        get_list_function = Media.objects.filter
        if self.context_var_name is not None:
            try:
                self.obj_id = self.context_var_name.resolve(context)
            except template.VariableDoesNotExist:
                return ''

        kwargs = {
            'entry__ff_id__exact': self.obj_id,
        }

        # Get a list of media attached to this entry
        media_list = get_list_function(**kwargs).order_by('-id').select_related()
        # Grab first media object
        try:
            first_media = media_list[0]
        except:
            # Get first MediaThumbnail object
            first_thumbnail =  first_media.mediathumbnail_set.all()[0]
            context[self.var_name] = first_thumbnail
        return ''

class MediaListNode(template.Node):
    def __init__(self, context_var_name, obj_id, var_name, limit=None):
        if context_var_name is not None:
            context_var_name = template.Variable(context_var_name)
        self.context_var_name, self.obj_id = context_var_name, obj_id
        self.var_name = var_name
        self.limit = limit

    def render(self, context):
        get_list_function = Media.objects.filter
        if self.context_var_name is not None:
            try:
                self.obj_id = self.context_var_name.resolve(context)
            except template.VariableDoesNotExist:
                return ''

        kwargs = {
            'entry__ff_id__exact': self.obj_id,
        }

        media_list = get_list_function(**kwargs).order_by('-id').select_related()
        if self.limit is None:
            context[self.var_name] = media_list
        else:
            context[self.var_name] = media_list[:self.limit]
        return ''

class CommentListNode(template.Node):
    def __init__(self, context_var_name, obj_id, var_name, by=None):
        if context_var_name is not None:
            context_var_name = template.Variable(context_var_name)
        self.context_var_name, self.obj_id = context_var_name, obj_id
        self.var_name = var_name
        self.by = by

    def render(self, context):
        get_list_function = Comment.objects.filter
        if self.context_var_name is not None:
            try:
                self.obj_id = self.context_var_name.resolve(context)
            except template.VariableDoesNotExist:
                return ''
        
        kwargs = {
            'entry__ff_id__exact': self.obj_id,
        }
        
        if self.by:
            kwargs['user__nickname__iexact'] = self.by
            
        comment_list = get_list_function(**kwargs).order_by('date').select_related()
        context[self.var_name] = comment_list
        return ''
     
class EntryListNode(template.Node):
    def __init__(self, filters, excludes, limit, var_name):
        self.filters = filters
        self.excludes = excludes
        self.limit = limit
        self.var_name = var_name
      
    def render(self, context):
        if 'all' in self.filters:
            entry_list = Entry.objects.all()
        else:
            entry_list = Entry.objects.filter(service__ff_id__in=self.filters)

        if self.excludes:
            entry_list = entry_list.exclude(service__ff_id__in=self.excludes)
        entry_list = entry_list.order_by('-published').select_related()
        
        if self.limit:
            entry_list = entry_list[:self.limit]
        context[self.var_name] = entry_list
        return ''
        
register.tag('get_friendfeed_entry_list', DoGetEntryList())
register.tag('get_friendfeed_comment_list', DoGetCommentList())
register.tag('get_friendfeed_media_list', DoGetMediaList())
register.tag('get_friendfeed_first_media_thumbnail', DoGetFirstMediaThumbnail())